#include "demo_utils.hpp"

#include <array>
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

namespace atomic_examples {
namespace {

/**
 * @brief 固定容量的单生产者单消费者环形缓冲区。
 *
 * @tparam T        缓冲区元素类型。
 * @tparam Capacity 缓冲区容量。
 *
 * 使用说明：
 *  - 利用两个原子索引 `write_index_` 与 `read_index_` 管理读写位置。
 *  - 生产者与消费者各自自旋等待，适用于极低延迟场景。
 *  - 仅在单生产者、单消费者时无锁安全，多生产者/消费者需额外同步。
 */
template <typename T, std::size_t Capacity>
class RingBuffer {
public:
  /**
   * @brief 将数据写入缓冲区。
   *
   * @param value 待写入的数据。
   * @return 若写入成功返回 true；若缓冲区满返回 false。
   */
  bool push(const T& value) {
    std::size_t write = write_index_.load(std::memory_order_relaxed);
    std::size_t next = (write + 1) % Capacity;

    if (next == read_index_.load(std::memory_order_acquire)) {
      return false;  ///< 队列已满，生产者需要等待。
    }

    buffer_[write] = value;
    write_index_.store(next, std::memory_order_release);  ///< 发布新写入的元素。
    return true;
  }

  /**
   * @brief 从缓冲区读取数据。
   *
   * @param value 用于存放读取结果的引用。
   * @return 若读取成功返回 true；若缓冲区空返回 false。
   */
  bool pop(T& value) {
    std::size_t read = read_index_.load(std::memory_order_relaxed);

    if (read == write_index_.load(std::memory_order_acquire)) {
      return false;  ///< 队列为空，消费者需等待。
    }

    value = buffer_[read];
    read_index_.store((read + 1) % Capacity, std::memory_order_release);
    return true;
  }

private:
    std::array<T, Capacity> buffer_{};             ///< 实际存储容器，固定容量数组。
    std::atomic<std::size_t> write_index_{0};      ///< 下一次写入的位置。
    std::atomic<std::size_t> read_index_{0};       ///< 下一次读取的位置。
};

/**
 * @brief 演示使用环形缓冲区进行生产者-消费者通信。
 *
 *  - 生产者写入 0~19 共 20 个整数。
 *  - 消费者持续读取直到收到全部数据。
 *  - 通过原子变量 `done` 通知生产者完成写入。
 */
void runRingBufferDemo() {
  printHeader("Atomic Ring Buffer Example");

  RingBuffer<int, 8> queue;
  std::atomic<bool> done{false};

  std::thread producer([&]() {
    for (int i = 0; i < 20; ++i) {
      while (!queue.push(i)) {
        std::this_thread::yield();  ///< 自旋等待时释放 CPU，降低忙等成本。
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }
    done.store(true, std::memory_order_release);
  });

  std::thread consumer([&]() {
    int value = 0;
    int count = 0;

    while (true) {
      if (queue.pop(value)) {
        std::cout << "Consumer got value: " << value << '\n';
        ++count;
      } else if (done.load(std::memory_order_acquire)) {
        while (queue.pop(value)) {
          std::cout << "Consumer got value: " << value << '\n';
          ++count;
        }
        break;
      } else {
        std::this_thread::yield();
      }
    }

    std::cout << "Consumer total count: " << count << '\n';
  });

  producer.join();
  consumer.join();
}

} // namespace
} // namespace atomic_examples

int main() {
  atomic_examples::runRingBufferDemo();
  return 0;
}